package cn.ac.iscas.base.utils;


import com.fasterxml.jackson.databind.ObjectMapper;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

public class ParameterUtils {


    public static Character getRequiredCharacter(Map data, String key) {
        Object valueObj = data.getOrDefault(key, null);
        if (valueObj == null) {
            throw new RuntimeException("No data for required key '" + key + "'");
        }
        try {
            Character character = (Character) valueObj;
            return character;
        } catch (ClassCastException e) {
            throw new RuntimeException("Data type of key '" + key + "'" + " error");
        }
    }

    public static Character getOrDefaultCharacter(Map data, String key, Character defaultVal) {
        if (data.getOrDefault(key, defaultVal).toString().length() < 1) {
            return defaultVal;
        }
        try {
            Character character = (Character) data.getOrDefault(key, defaultVal);
            return character;
        } catch (ClassCastException e) {
            throw new RuntimeException("Data type of key '" + key + "'" + " error");
        }
    }

    public static Double getRequiredDouble(Map data, String key) {
        Object valueObj = data.getOrDefault(key, null);
        if (valueObj == null) {
            throw new RuntimeException("No data for required key '" + key + "'");
        }
        try {
            return Double.parseDouble(valueObj.toString());
        } catch (NumberFormatException e) {
            throw new RuntimeException("Data type of key '" + key + "'" + " error");
        }
    }

    public static Double getOrDefaultDouble(Map data, String key, Double defaultVal) {
        Object valueObj = data.getOrDefault(key, defaultVal);
        if (valueObj == null) {
            throw new RuntimeException("No data for required key '" + key + "'");
        }
        try {
            return Double.parseDouble(valueObj.toString());
        } catch (NumberFormatException e) {
            throw new RuntimeException("Data type of key '" + key + "'" + " error");
        }
    }

    public static Integer getOrDefaultInt(Map data, String key, Integer defaultVal) {
        Object valueObj = data.getOrDefault(key, defaultVal);
        if (valueObj == null) {
            throw new RuntimeException("No data for required key '" + key + "'");
        }
        try {
            return Integer.parseInt(valueObj.toString());
        } catch (NumberFormatException e) {
            throw new RuntimeException("Data type of key '" + key + "'" + " error");
        }
    }

    public static long getOrDefaultLong(Map data, String key, long defaultVal) {
        Object valueObj = data.getOrDefault(key, defaultVal);
        if (valueObj == null) {
            throw new RuntimeException("No data for required key '" + key + "'");
        }
        try {
            return Long.parseLong(valueObj.toString());
        } catch (NumberFormatException e) {
            throw new RuntimeException("Data type of key '" + key + "'" + " error");
        }
    }

    public static Integer getRequiredInt(Map data, String key) {
        Object valueObj = data.getOrDefault(key, null);
        if (valueObj == null) {
            throw new RuntimeException("No data for required key '" + key + "'");
        }
        try {
            return Integer.parseInt(valueObj.toString());
        } catch (NumberFormatException e) {
            throw new RuntimeException("Data type of key '" + key + "'" + " error");
        }
    }

    public static String[] getRequiredStringArray(Map data, String key, String splitChar) {
        Object valueObj = data.getOrDefault(key, null);
        if (valueObj == null) {
            throw new RuntimeException("No data for required key '" + key + "'");
        }
        if (valueObj instanceof List) {
            List<Object> objectList = (List)valueObj;
            if (objectList.isEmpty()) {
                throw new RuntimeException("No data for required key '" + key + "'");
            }
            return (String[])objectList.toArray();
        } else if (valueObj instanceof String) {
            String value = valueObj.toString();
            if (value.isEmpty()) {
                throw new RuntimeException("No data for required key '" + key + "'");
            }
            if (splitChar == null || splitChar.isEmpty()) {
                return new String[]{value};
            }
            return value.split(splitChar);
        } else {
            throw new RuntimeException("Data type of key '" + key + "'" + " error");
        }
    }

    public static String getRequiredString(Map data, String key) {
        Object valueObj = data.getOrDefault(key, null);
        if (valueObj == null) {
            throw new RuntimeException("No data for required key '" + key + "'");
        }
        if (valueObj instanceof List) {
            List<Object> objectList = (List)valueObj;
            if (objectList.isEmpty()) {
                throw new RuntimeException("No data for required key '" + key + "'");
            }
            String value = objectList.get(0).toString();
            if (value.isEmpty()) {
                throw new RuntimeException("No data for required key '" + key + "'");
            }
            return value;
        } else if (valueObj instanceof String) {
            String value = valueObj.toString();
            if (value.isEmpty()) {
                throw new RuntimeException("No data for required key '" + key + "'");
            }
            return value;
        } else {
            throw new RuntimeException("Data type of key '" + key + "'" + " error");
        }
    }

    public static String[] getRequiredStringArray(Map data, String key) {
        Object valueObj = data.getOrDefault(key, null);
        if (valueObj == null) {
            throw new RuntimeException("No data for required key '" + key + "'");
        }
        if (valueObj instanceof List) {
            List<Object> objectList = (List)valueObj;
            if (objectList.isEmpty()) {
                throw new RuntimeException("No data for required key '" + key + "'");
            }
            return (String[]) objectList.toArray();
        } else if (valueObj instanceof String) {
            String value = valueObj.toString();
            if (value.isEmpty()) {
                throw new RuntimeException("No data for required key '" + key + "'");
            }
            return value.split(",");
        } else {
            throw new RuntimeException("Data type of key '" + key + "'" + " error");
        }
    }

    public static Integer[] getRequiredIntArray(Map data, String key) {
        Object valueObj = data.getOrDefault(key, null);
        if (valueObj == null) {
            throw new RuntimeException("No data for required key '" + key + "'");
        }
        if (valueObj instanceof List) {
            List<Object> objectList = (List)valueObj;
            if (objectList.isEmpty()) {
                throw new RuntimeException("No data for required key '" + key + "'");
            }
            return (Integer[]) objectList.toArray();
        } else if (valueObj instanceof String) {
            String value = valueObj.toString();
            if (value.isEmpty()) {
                throw new RuntimeException("No data for required key '" + key + "'");
            }
            String[] strArray = value.split(",");
            Integer[] result = new Integer[strArray.length];
            for (int i = 0; i < strArray.length; i++) {
                result[i] = Integer.parseInt(strArray[i]);
            }
            return result;
        } else {
            throw new RuntimeException("Data type of key '" + key + "'" + " error");
        }
    }

//    public static Integer[] getOrDefaultIntArray(Map data, String key, Integer[] defaultVal) {
//        Object valueObj = data.getOrDefault(key, defaultVal);
//        if (valueObj == null) {
//            throw new RuntimeException("No data for required key '" + key + "'");
//        }
//        return (Integer[])valueObj;
//    }

    public static int[] getOrDefaultIntArray(Map data, String key, int[] defaultVal) {
        Object valueObj = data.getOrDefault(key, defaultVal);
        if (valueObj == null) {
            throw new RuntimeException("No data for required key '" + key + "'");
        }
        List va = (ArrayList)valueObj;
        int[] val = new int[va.size()];
        for(int i = 0; i < va.size(); i++) {
            val[i] = (int)va.get(i);
        }
        return val;
    }


    public static String getOrDefaultString(Map data, String key, String defaultVal) {
        Object valueObj = data.getOrDefault(key, defaultVal);
        if (valueObj == null) {
            throw new RuntimeException("No data for required key '" + key + "'");
        }
        if (valueObj instanceof List) {
            List<Object> objectList = (List)valueObj;
            if (objectList.isEmpty()) {
                throw new RuntimeException("No data for required key '" + key + "'");
            }
            String value = objectList.get(0).toString();
            if (value.isEmpty()) {
                throw new RuntimeException("No data for required key '" + key + "'");
            }
            return value;
        } else if (valueObj instanceof String) {
            String value = valueObj.toString();
//            if (value.isEmpty()) {
//                throw new RuntimeException("No data for required key '" + key + "'");
//            }
            return value;
        } else {
            throw new RuntimeException("Data type of key '" + key + "'" + " error");
        }
    }

    public static Boolean getRequiredBoolean(Map data, String key) {
        Object valueObj = data.getOrDefault(key, null);
        if (valueObj == null) {
            throw new RuntimeException("No data for required key '" + key + "'");
        }
        if (valueObj instanceof Boolean) {
            return Boolean.valueOf(valueObj.toString());
        } else {
            throw new RuntimeException(String.format("Data type of key: '%s' is error, require boolean", key));
        }
    }

    public static Boolean getOrDefaultBoolean(Map data, String key, Boolean defaultVal) {
        Object valueObj = data.getOrDefault(key, defaultVal);
        if (valueObj == null) {
            throw new RuntimeException("No data for required key '" + key + "'");
        }
        if (valueObj instanceof Boolean) {
            return Boolean.valueOf(valueObj.toString());
        } else {
            throw new RuntimeException(String.format("Data type of key: '%s' is error, require boolean", key));
        }
    }

    public static Boolean getBoolean(Map data, String key) {
        Object valueObj = data.getOrDefault(key, null);
        if (valueObj == null) {
            return false;
        } else {
            if (valueObj instanceof Boolean) {
                return Boolean.valueOf(valueObj.toString());
            } else {
                throw new RuntimeException(String.format("Data type of key: '%s' is error, require boolean", key));
            }
        }
    }

    public static Map getRequiredMap(Map data, String key) {
        Object valueObj = data.getOrDefault(key, null);
        if (valueObj == null) {
            throw new RuntimeException("No data for required key '" + key + "'");
        }
        try {
            return (Map)(valueObj);
        } catch (NumberFormatException e) {
            throw new RuntimeException("Data type of key '" + key + "'" + " error");
        }
    }

    public static List getRequiredList(Map data, String key) {
        Object valueObj = data.getOrDefault(key, null);

        if (valueObj == null) {
            throw new RuntimeException("No data for required key '" + key + "'");
        }
        try {
            return (List)(valueObj);
        } catch (NumberFormatException e) {
            throw new RuntimeException("Data type of key '" + key + "'" + " error");
        }

    }


    public static<T> List<T> getRequiredListObj(Map data, String key, Class<T> cls) {
        Object valueObj = data.getOrDefault(key, null);
        final ObjectMapper mapper = new ObjectMapper();
        if (valueObj == null) {
            throw new RuntimeException("No data for required key '" + key + "'");
        }
        try {
            List<Object> list = (List)(valueObj);
            List<T> result = new ArrayList<>();
            for (Object objs : list) {
                result.add(cls.cast(mapper.convertValue(objs, cls)));
            }
            return result;
        } catch (NumberFormatException e) {
            throw new RuntimeException("Data type of key '" + key + "'" + " error");
        }
    }

    public static<T> T getRequiredObj(Map data, String key, Class<T> cls) {
        Object valueObj = data.getOrDefault(key, null);
        if (valueObj == null) {
            throw new RuntimeException("No data for required key '" + key + "'");
        }
        final ObjectMapper mapper = new ObjectMapper();
        return cls.cast(mapper.convertValue(valueObj, cls));
    }


}
